/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						script_targeted_action.cpp

	$Header: /heroes4/script_targeted_action.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "script_targeting.h"

#include "adventure_frame.h"
#include "adventure_map.h"
#include "army.h"
#include "hero.h"
#include "town.h"

t_hero const* select_hero_by_target(t_script_hero_target target, t_creature_array const* garrison, t_hero const* hero, t_creature_array const* army, t_creature_array const* opposing_army)
{
	if (garrison && (army == NULL))
		army = opposing_army;

	switch (target) {
		case k_script_hero_target_this:
			return hero;

		case k_script_hero_target_this_army_any:
			if (army)
				return army->get_any_hero();
			else 
				if (garrison) 
					return opposing_army ? opposing_army->get_any_hero() : NULL;
			return NULL;

		case k_script_hero_target_garrison_any:
			return garrison ? garrison->get_any_hero() : NULL;

		case k_script_hero_target_opposing_army_any:
			return opposing_army ? opposing_army->get_any_hero() : NULL;

		case k_script_hero_target_this_army_most_powerful:
			return army ? army->get_strongest_hero() : NULL;

		case k_script_hero_target_garrison_most_powerful:
			return garrison ? garrison->get_strongest_hero() : NULL;

		case k_script_hero_target_opposing_army_most_powerful:
			return opposing_army ? opposing_army->get_strongest_hero() : NULL;

		case k_script_hero_target_this_army_least_powerful:
			return army ? army->get_weakest_hero() : NULL;

		case k_script_hero_target_garrison_least_powerful:
			return garrison ? garrison->get_weakest_hero() : NULL;

		case k_script_hero_target_opposing_army_least_powerful:
			return opposing_army ? opposing_army->get_weakest_hero() : NULL;

		default:
			assert(false);
			return NULL;
	}
}

t_hero* select_hero_by_target(t_script_hero_target target, t_creature_array* garrison, t_hero* hero, t_creature_array* army, t_creature_array* opposing_army)
{
	return const_cast<t_hero*>( select_hero_by_target( target, static_cast<t_creature_array const*>(garrison), hero, army, opposing_army ) );
}

t_player const* select_player_by_target(t_script_player_target target, t_adventure_map const* map, t_owned_adv_object const* adv_object, t_player const* owner, t_player const* opposing_army_owner)
{
	switch(target) {
		case k_script_player_target_owner:
			if (owner)
				return owner;
			else 
				return adv_object ? adv_object->get_owner() : NULL;
		case k_script_player_target_current:
			assert(map);
			return map ? &map->get_current_player() : 0;
		case k_script_player_target_opposing:
			return opposing_army_owner;
		default:
			assert(map && (target < k_script_player_target_count));
			return map ? map->get_player(get_player_color(target)) : 0;
	}
}

t_player* select_player_by_target(t_script_player_target target, t_adventure_map* map, t_owned_adv_object* adv_object, t_player* owner, t_player* opposing_army_owner)
{
	return const_cast<t_player*>( select_player_by_target(target, static_cast<t_adventure_map const*>(map), adv_object, owner, opposing_army_owner) );
}

t_creature_array const* select_creature_array_by_target(t_script_stack_target target, t_creature_array const* garrison, t_creature_array const* army, t_creature_array const* opposing_army)
{
	switch(target) {
		case k_script_stack_target_this:
			assert(false);
			return NULL;
		case k_script_stack_target_garrison:
			return garrison;
		case k_script_stack_target_this_army:
			if (army)
				return army;
			else
				if (garrison) 
					return opposing_army;
			return NULL;
		case k_script_stack_target_opposing_army:
			return opposing_army;
		default:
			assert(false);
			return NULL;
	}
}

t_creature_array* select_creature_array_by_target(t_script_stack_target target, t_creature_array* garrison, t_creature_array* army, t_creature_array* opposing_army)
{
	return const_cast<t_creature_array*>( select_creature_array_by_target( target, static_cast<t_creature_array const*>(garrison), army, opposing_army ) );
}

t_creature_array const* select_army_by_target(t_script_army_target target, t_creature_array const* garrison, t_creature_array const* army, t_creature_array const* opposing_army)
{
	switch(target) {
		case k_script_army_target_this:
			if (army) 
				return army;
			else
				if (garrison) 
					return opposing_army;
			return NULL;
		case k_script_army_target_garrison:
			return garrison;
		case k_script_army_target_opposing:
			return opposing_army;
		default:
			assert(false);
			return NULL;
	}

}

t_creature_array* select_army_by_target(t_script_army_target target, t_creature_array* garrison, t_creature_array* army, t_creature_array* opposing_army)
{
	return const_cast<t_creature_array*>( select_army_by_target(target, static_cast<t_creature_array const*>(garrison), army, opposing_army) );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void post_execute_validate(t_creature_array* array)
{
	if (array == NULL) 
		return;
	array->check_hero_levels();

	t_army* army = array->get_army();

	if (army) 
	{
		army->get_adventure_frame()->update_army( army );
	}
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void execute_hero_script( t_hero_scriptable_event event, t_script_context_hero const& context, t_creature_array const * losses )
{
	if (context.map->is_game_over())
		return;
	bool potential_update = false;
	assert(context.army && context.map);
	for (int i = 0; i < t_creature_array::k_size; i++) 
	{
		t_hero* hero = (*context.army)[i].get_hero();

		if (hero) 
		{
			// Ignore heroes who were dead at start of combat
			// Note: This happens before heroes are taken prisoner
			// & resurrected in prison; the dead heroes will still
			// be dead
			assert( !hero->is_imprisoned() );

			if ( hero->is_dead() )
			{
				if ( losses != NULL )
				{
					bool already_dead = true;
					for ( int j = 0; already_dead && j < t_creature_array::k_size; j++ )
					{
						if (   (losses[0][j].get_const_hero() == hero) 
							|| (losses[1][j].get_const_hero() == hero))
						{
							already_dead = false;
						}
					}

					if ( already_dead )
						continue;
				}
			}
			else
			{
				// Only apply k_hero_scriptable_lose_combat if the hero actually 
				// died. This is a major hack, but the code is just not set up
				// to handle this better.
				if ( event == k_hero_scriptable_lose_combat )
					continue;
			}

			t_script_context_hero hero_context = context;
			hero_context.hero = hero;
			hero->execute_script(event, hero_context);
			potential_update = true;
			if (context.map->is_game_over())
				return;
		}
	}
	
	if (potential_update) 
	{
		t_adventure_frame* frame = context.army->get_adventure_frame();

		frame->update();
		post_execute_validate( context.army );
		post_execute_validate( context.opposing_army );

		context.map->process_continuous_events( frame );
	}
}
